using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Microsoft.WowAddonStudio.Tests.Common.Mocks
{
    /// <summary>
	/// Provides access to a service identified by a globally unique identifier (GUID). For more information, see IServiceProvider.
    /// </summary>
	public class SimpleTestServiceLocator : IDependencyResolver, VisualStudio.OLE.Interop.IServiceProvider, ISite, IContainer, IServiceContainer
    {
        private static readonly Dictionary<Type, object> serviceContainer = new Dictionary<Type, object>();
		private static readonly List<IComponent> components = new List<IComponent>();

    	#region ISite Members

		/// <summary>
		/// Gets the component associated with the <see cref="T:System.ComponentModel.ISite"/> when implemented by a class.
		/// </summary>
		/// <value></value>
		/// <returns>The <see cref="T:System.ComponentModel.IComponent"/> instance associated with the <see cref="T:System.ComponentModel.ISite"/>.</returns>
    	public IComponent Component
    	{
    		get { return null; }
    	}

		/// <summary>
		/// Gets the <see cref="T:System.ComponentModel.IContainer"/> associated with the <see cref="T:System.ComponentModel.ISite"/> when implemented by a class.
		/// </summary>
		/// <value></value>
		/// <returns>The <see cref="T:System.ComponentModel.IContainer"/> instance associated with the <see cref="T:System.ComponentModel.ISite"/>.</returns>
    	public IContainer Container
    	{
    		get { return this; }
    	}

		/// <summary>
		/// Determines whether the component is in design mode when implemented by a class.
		/// </summary>
		/// <value></value>
		/// <returns>true if the component is in design mode; otherwise, false.</returns>
    	public bool DesignMode
    	{
    		get { return false; }
    	}

    	/// <summary>
    	/// Gets or sets the name of the component associated with the <see cref="T:System.ComponentModel.ISite"/> when implemented by a class.
    	/// </summary>
    	/// <value></value>
    	/// <returns>The name of the component associated with the <see cref="T:System.ComponentModel.ISite"/>; or null, if no name is assigned to the component.</returns>
    	public string Name { get; set; }

    	#endregion

		/// <summary>
		/// Resets this instance.
		/// </summary>
        public static void Reset()
        {
            serviceContainer.Clear();
			components.Clear();
        }

		/// <summary>
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="service"></param>
		public static void Register<T>(T service)
		{
			if (serviceContainer.ContainsKey(typeof (T)))
				serviceContainer[typeof (T)] = service;
			else
				serviceContainer.Add(typeof (T), service);
		}


		/// <summary>
		/// Unregister the specified service.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		public static void UnRegister<T>()
		{
			if (serviceContainer.ContainsKey(typeof(T)))
				serviceContainer.Remove(typeof(T));
		}

		/// <summary>
		/// Removes this instance.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		public void Remove<T>()
		{
			if (serviceContainer.ContainsKey(typeof (T)))
				serviceContainer.Remove(typeof (T));
		}

		/// <summary>
		/// Gets this instance.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <returns></returns>
		public static T Get<T>()
		{
			object service;
			serviceContainer.TryGetValue(typeof (T), out service);
			return (T) service;
		}

		#region IServiceProvider Members

		/// <summary>
		/// Gets the service.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <returns></returns>
		public T GetService<T>()
		{
			return (T) GetService(typeof (T));
		}

    	/// <summary>
		/// Gets the service object of the specified type.
		/// </summary>
		/// <param name="serviceType">An object that specifies the type of service object to get.</param>
		/// <returns>
		/// A service object of type <paramref name="serviceType"/>.-or- null if there is no service object of type <paramref name="serviceType"/>.
		/// </returns>
		public object GetService(Type serviceType)
		{
			object service;
			serviceContainer.TryGetValue(serviceType, out service);
			return service;
		}

		#endregion


		/// <summary>
		/// </summary>
		/// <param name="guidService"></param>
		/// <param name="riid"></param>
		/// <param name="ppvObject"></param>
		/// <returns></returns>
		public int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject)
		{
			Trace.WriteLine("Mock QueryService..." + guidService);
			ppvObject = IntPtr.Zero;
			foreach (KeyValuePair<Type, object> valuePair in serviceContainer)
			{
				if (valuePair.Key.GUID == guidService)
				{
					ppvObject = Marshal.GetIUnknownForObject(valuePair.Value);
					Trace.WriteLine("ppvObject..." + ppvObject);
					break;
				}
			}
			return 0;
		}

		#region IDependencyResolver Members

		/// <summary>
		/// Registers the specified service.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="service">The service.</param>
		void IDependencyResolver.Register<T>(T service)
		{
			Register(service);
		}

		#endregion
	
    	#region IContainer Members

		/// <summary>
		/// Adds the specified <see cref="T:System.ComponentModel.IComponent"/> to the <see cref="T:System.ComponentModel.IContainer"/> at the end of the list.
		/// </summary>
		/// <param name="component">The <see cref="T:System.ComponentModel.IComponent"/> to add.</param>
    	public void Add(IComponent component)
    	{
    		if(!components.Contains(component))
				components.Add(component);
    	}

		/// <summary>
		/// Adds the specified <see cref="T:System.ComponentModel.IComponent"/> to the <see cref="T:System.ComponentModel.IContainer"/> at the end of the list, and assigns a name to the component.
		/// </summary>
		/// <param name="component">The <see cref="T:System.ComponentModel.IComponent"/> to add.</param>
		/// <param name="name">The unique, case-insensitive name to assign to the component.-or- null that leaves the component unnamed.</param>
    	public void Add(IComponent component, string name)
    	{
			if (!components.Contains(component))
				components.Add(component);
    	}

		/// <summary>
		/// Removes a component from the <see cref="T:System.ComponentModel.IContainer"/>.
		/// </summary>
		/// <param name="component">The <see cref="T:System.ComponentModel.IComponent"/> to remove.</param>
    	public void Remove(IComponent component)
    	{
			if (components.Contains(component))
				components.Remove((component));
    	}

		/// <summary>
		/// Gets all the components in the <see cref="T:System.ComponentModel.IContainer"/>.
		/// </summary>
		/// <value></value>
		/// <returns>A collection of <see cref="T:System.ComponentModel.IComponent"/> objects that represents all the components in the <see cref="T:System.ComponentModel.IContainer"/>.</returns>
    	public ComponentCollection Components
    	{
			get { return new ComponentCollection(components.ToArray()); }
    	}

    	#endregion

    	#region IDisposable Members

		/// <summary>
		/// Performs application-defined tasks associated with freeing,
		/// releasing, or resetting unmanaged resources.
		/// </summary>
    	public void Dispose()
    	{
    		
    	}

    	#endregion


		/// <summary>
		/// Adds the service.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="serviceInstance">The service instance.</param>
		public void AddService<T>(object serviceInstance)
		{
			AddService(typeof(T), serviceInstance, false);
		}

		/// <summary>
		/// Adds the specified service to the service container.
		/// </summary>
		/// <param name="serviceType">The type of service to add.</param>
		/// <param name="serviceInstance">An instance of the service type to add. This object must implement or inherit from the type indicated by the <paramref name="serviceType"/> parameter.</param>
    	public void AddService(Type serviceType, object serviceInstance)
    	{
    		AddService(serviceType, serviceInstance, false);
    	}

		/// <summary>
		/// Adds the specified service to the service container, and optionally promotes the service to any parent service containers.
		/// </summary>
		/// <param name="serviceType">The type of service to add.</param>
		/// <param name="serviceInstance">An instance of the service type to add. This object must implement or inherit from the type indicated by the <paramref name="serviceType"/> parameter.</param>
		/// <param name="promote">true to promote this request to any parent service containers; otherwise, false.</param>
    	public void AddService(Type serviceType, object serviceInstance, bool promote)
    	{
			if (!serviceContainer.ContainsKey(serviceType))
			{
				serviceContainer.Add(serviceType, serviceInstance);
			}
    	}

		/// <summary>
		/// Adds the specified service to the service container.
		/// </summary>
		/// <param name="serviceType">The type of service to add.</param>
		/// <param name="callback">A callback object that is used to create the service. This allows a service to be declared as available, but delays the creation of the object until the service is requested.</param>
    	public void AddService(Type serviceType, ServiceCreatorCallback callback)
    	{
			AddService(serviceType, callback, false);
    	}

		/// <summary>
		/// Adds the specified service to the service container, and optionally promotes the service to parent service containers.
		/// </summary>
		/// <param name="serviceType">The type of service to add.</param>
		/// <param name="callback">A callback object that is used to create the service. This allows a service to be declared as available, but delays the creation of the object until the service is requested.</param>
		/// <param name="promote">true to promote this request to any parent service containers; otherwise, false.</param>
    	public void AddService(Type serviceType, ServiceCreatorCallback callback, bool promote)
    	{
    		var serviceInstance = callback.Invoke(this, serviceType);
			AddService(serviceType, serviceInstance, false);
    	}

		/// <summary>
		/// Removes the specified service type from the service container.
		/// </summary>
		/// <param name="serviceType">The type of service to remove.</param>
    	public void RemoveService(Type serviceType)
    	{
    		RemoveService(serviceType, false);
    	}

		/// <summary>
		/// Removes the specified service type from the service container, and optionally promotes the service to parent service containers.
		/// </summary>
		/// <param name="serviceType">The type of service to remove.</param>
		/// <param name="promote">true to promote this request to any parent service containers; otherwise, false.</param>
    	public void RemoveService(Type serviceType, bool promote)
    	{
			if (serviceContainer.ContainsKey(serviceType))
			{
				serviceContainer.Remove(serviceType);
			}
    	}
    }
}
